Post ESP Intune Win32 apps installations

In enterprise environments, we have to deal with a lot of requirements when it comes to app management. One of the common challenges is to control the installation moment during enrollment. We already have some basic controls in place. If the Enrollment Status Page (ESP) is configured during the enrollment all device targeted apps are installed in the “Device Setup” phase and all the user targeted apps are installed in the “Account Setup” phase. These are simple moments in time already. But what if you would like to schedule the installation after ESP when the Desktop is fully available and displayed?

The goal is to let a required Win32 app deployed via Intune install only if the ESP is not running anymore. Why? Maybe because we want to display a dialog for user interaction and this is only possible when the Desktop is fully loaded, otherwise the dialog will be hidden behind the ESP and can’t be interacted with. Resulting in an ESP waiting infinitely for an app which is hidden in the background :-(.

In fact, I needed this for my BitLocker PIN solution (How to enable Pre-Boot BitLocker startup PIN on Windows with Intune) already in the past but didn’t come up with a good idea how to solve it in a reliable way.

interactive BitLocker PIN dialog during ESP

Now I gave it another try and solved it by doing the following.

UPDATE 10/20/202:

The following approach worked well in my test environment, but not during the final implementation! The initial idea is used somewhere else where it is working successfully with a different precondition, but in the Autopilot requirement script, there is one caveat I found after releasing the blog. The following paragraphs still describe the way I approached the problem and finally also the issue discovered later with it. If you want to jump to the final solution, click here.

Similar to the Win32 App Requirement Script for Primary User Detection (Deploy an Intune application with user device affinity) I wrote a Win32 App Requirement Script to detect the running ESP. The script is looking for the WWAHost process which is responsible for UWP apps to run JavaScript code. The ESP is based on a UWP app implementation using JavaScript. To accomplish this task, the WWAHost has to load the CloudExperienceHost Module for this to actually display the ESP to the user. So, if the WWAHost is running and has the CloudExperienceHost module loaded, Windows will assign a MainWindowHanldle to the process as soon as the process displays a Window. This is what we can query from any context (user context or system context) by enumerating the MainWindowHandle from the process:

$(Get-Process -Name WWAHost).MainWindowHandle

If this gives us a value other than 0 the process is showing a Window currently.

The Requirement Script finally looks like this:

Detect-ActiveESP.ps1

To demonstrate the whole story, here are a few screenshots of how this all works.

First, I checked if the theory actually works, so execution of the code in system context during the ESP “Account Setup” phase. We see the result of active:

Detect the active ESP via PowerShell

I used my BitLocker PIN solution and then added the new requirement script to it and voila, it did what we expected from theory. The execution was prevented during ESP and first allowed after ESP wasn’t detected anymore (you can see the full Desktop in the background).

Detect-ActiveESP.ps1 success in IME logs and BitLocker PIN solution started

To get this result I simply added the Requirement Script to the BitLocker PIN solution like this:

Detect-ActiveESP.ps1 added as Requirement script to Win32 app

Adding the Detect-ActiveESP.ps1 and do a string compare to ESP-not-active.

As l already demonstrated the BitLocker script was first shown when the full Desktop was loaded:

BitLocker PIN solution started with requirement rule Detect-ActiveESP.ps1

UPDATE 10/20/202 – changed approach:

The issue with this solution is that my test approach was altering the environment. By utilizing the [Shift] + [F10] hotkey to get a system command prompt I changed the preconditions! As soon as you enter the [Shift] + [F10] hotkey, the explorer.exe is loaded and this is a different precondition for my requirement script. I verified the script by utilizing this approach and it worked perfectly by executing the code in the command prompt. You may ask now: What is the issue? As soon as I started to use the code in the requirement script without the [Shift] + [F10] I was using during verification, it wasn’t working anymore. After digging deeper, I found this:

MS Learn Diagnostics.MainWindowHandle

A process has a main window associated with it only if the process has a graphical interface. If the associated process does not have a main window, the MainWindowHandle value is zero. The value is also zero for processes that have been hidden, that is, processes that are not visible in the taskbar. This can be the case for processes that appear as icons in the notification area, at the far right of the taskbar.

This explains why my command prompt tests were successful as explorer.exe was loaded.

So, I gave it another try to find a different indicator when the desktop is loaded. It is not an elegant version (imho) and has some downsides, but it ist working for now. I simply take the “Windows Security notification icon” as an indicator for now. This icon is first started when the explorer fully loads in the User Desktop session:

Windows Security notification icon

The downside is, that the security icon should not be hidden by policy:

HKLM\SOFTWARE\Policies\Microsoft\Windows Defender Security Center\Systray\HideSystray = 1

As long as this precondition is given the solutions works fine for me.

You can find the updated simple Requirement Script on my GitHub:

https://github.com/okieselbach/Intune/blob/master/Detect-ActiveESP.ps1

I hope this solves a challenge that is coming up often. At least for me, it is useful and a real-world use case I have shown with the BitLocker PIN solution. I hope also it demonstrates how even your test approach may not always be sufficient. Feel free to leave a comment about what you have used it for or if you found a better solution!

Happy deployment!